wiki:RarSourceCodes

Version 3 (modified by guest, 15 years ago) (diff)

Added Ruby (not done yet)

Programming Languages

  1. Python
  2. Ruby

These snippets should be useful to read RAR archives with store compression (which means none compression is used). Feel free to edit/add source-codes if you have faster/better/new language implementation.

Links:

Python

#(c)Kamil Dzióbek turbos11(at)gmail.com
#   BSD License

from struct import *

def getlastsplit(firsrarfile,x):
    if firsrarfile[-3:]=='001':
        return firsrarfile[:-3]+('%03d' %(x+1))
    if firsrarfile[-11:-6]=='.part':
        return firsrarfile[0:-6]+('%02d' % (x+1))+firsrarfile[-4:]
    if firsrarfile[-10:-5]=='.part':
        return firsrarfile[0:-5]+('%1d' % (x+1))+firsrarfile[-4:]
    return firsrarfile[0:-2]+('%02d' %(x-1) )

def addfilehash(name,hash,seek):
    f=open(name, 'rb')
    f.seek(seek)
    for i in range(8192):
        hash+=unpack('<q', f.read(8))[0]
        hash =hash & 0xffffffffffffffff
    return hash


def OpensubtitlesHashRar(firsrarfile):
    """Hash file in rar split archive. -> (size,hash)"""
    if not(firsrarfile.lower().endswith('.rar') | firsrarfile.endswith('.001')):
        raise Exception('Bad file suffix. Only .rar and .001 support.')
    if firsrarfile[-11:-6]=='.part':
        firsrarfile=firsrarfile[0:-6]+'01'+firsrarfile[-4:]
    f=open(firsrarfile, 'rb')
    a=f.read(4)
    if a!='Rar!':
        raise Exception('ERROR: This is not rar file.')
    seek=0
    for i in range(4):
        f.seek(seek)
        a=f.read(100)
        type,flag,size=unpack( '<BHH', a[2:2+5]) 
        if 0x74==type:
            if 0x30!=unpack( '<B', a[25:25+1])[0]:
                raise Exception('Bad compression method! Work only for "store".')            
            s_partiizebodystart=seek+size
            s_partiizebody,s_unpacksize=unpack( '<II', a[7:7+2*4])
            if (flag & 0x0100):
                s_unpacksize=(unpack( '<I', a[36:36+4])[0] <<32 )+s_unpacksize
                print 'Hash untested for files biger that 2gb. May work or may generate bad hash.'
            lastrarfile=getlastsplit(firsrarfile,(s_unpacksize-1)/s_partiizebody)
            hash=addfilehash(firsrarfile,s_unpacksize,s_partiizebodystart)
            hash=addfilehash(lastrarfile,hash,(s_unpacksize%s_partiizebody)+s_partiizebodystart-65536)
            return (s_unpacksize,"%016x" % hash )
        seek+=size
    raise Exception('ERROR: Not Body part in rar file.')

#example
firsrarsplitfile='I:\\test.rar'
size,hash=OpensubtitlesHashRar(firsrarsplitfile)
print 'use this valuse on opensubtiles.com\n\tsize is: %d\n\thash is: %s' % (size,hash)

Ruby

Noncomplete translation of Python-code.

def getlastsplit(firsrarfile, x)
    return firsrarfile[0..-3] + ('%03d' % (x+1)) if firsrarfile[-3..-1] == '001'
    return firsrarfile[0..-6] + ('%02d' % (x+1)) + firsrarfile[-4..-1] if firsrarfile[-11..-6] == '.part'
    return firsrarfile[0..-5] + ('%1d' % (x+1)) + firsrarfile[-4..-1] if firsrarfile[-10..-5] == '.part'
    return firsrarfile[0..-2] + ('%02d' % (x-1))
end

def addfilehash(name, hash, seek)
    f = File.open(name, 'rb')
    f.seek(seek)
    8192.times do |i|
        hash += f.read(8).unpack('q')[0]
        hash = hash & 0xffffffffffffffff
    end
    return hash
end

# Hash file in rar split archive. -> (size,hash)
def OpensubtitlesHashRar(firsrarfile)
    raise 'Bad file suffix. Only .rar and .001 support.' unless firsrarfile.downcase =~ /\.rar$/ || firsrarfile  =~ /\.001$/
        
    firsrarfile = firsrarfile[0..-6] + '01' + firsrarfile[-4..-1] if firsrarfile[-11..-6] == '.part'
        
    f = File.open(firsrarfile, 'rb')
    a = f.read(4)
    raise 'ERROR: This is not rar file.' if a != 'Rar!'
        
    seek = 0
    4.times do |i|
        f.seek(seek)
        a = f.read(100)
        type, flag, size = a[2..7].unpack('Cvv') 
	puts [type, flag, size].inspect
        if 0x74 == type
            raise 'Bad compression method! Work only for "store".' if 0x30 != a[25..26].unpack('C')[0]
                
            s_partiizebodystart = seek + size
            s_partiizebody, s_unpacksize = a[7..15].unpack('<II')
            if flag & 0x0100
                s_unpacksize = (a[36..40].unpack('<I')[0] << 32) + s_unpacksize
                puts 'Hash untested for files biger that 2gb. May work or may generate bad hash.'
            end
            lastrarfile = getlastsplit(firsrarfile, (s_unpacksize-1) / s_partiizebody)
            hash = addfilehash(firsrarfile, s_unpacksize, s_partiizebodystart)
            hash = addfilehash(lastrarfile, hash, (s_unpacksize % s_partiizebody) + s_partiizebodystart - 65536)
            return [s_unpacksize, "%016x" % hash]
        end
        seek += size
    end
    raise 'ERROR: Not Body part in rar file.'
end

#example
firsrarsplitfile=ARGV[0]
size, hash = OpensubtitlesHashRar(firsrarsplitfile)
puts('use this valuse on opensubtiles.com\n\tsize is: %d\n\thash is: %s' % [size, hash])